package ca.ubc.icapture.genapha.actions;

import ca.ubc.icapture.common.util.SnpXYURLGenerator;
import ca.ubc.icapture.genapha.forms.SearchAssociationForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.Action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import icapture.SQLMgr;
import icapture.beans.DB.Cohort;
import icapture.beans.DB.Gene;
import icapture.beans.DB.Phenotypes;
import icapture.beans.DB.SNP;
import icapture.beans.DB.Values;
import icapture.beans.KeggGeneBean;
import icapture.beans.KeggPathwayBean;
import icapture.genapha.GenotypeToolsManager;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import javax.servlet.http.HttpSession;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMessage;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.StandardEntityCollection;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.LengthAdjustmentType;
import org.jfree.ui.RectangleAnchor;
import org.jfree.ui.TextAnchor;

/**
 * Created by IntelliJ IDEA.
 * User: btripp
 * Date: Sep 13, 2006
 * Time: 9:39:08 AM
 * To change this template use File | Settings | File Templates.
 */
public class SearchAssociationAction extends Action
{
    @Override
    public ActionForward execute(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
    {

        ActionForward forward = new ActionForward();
        HttpSession session = request.getSession();
        SearchAssociationForm searchForm = (SearchAssociationForm) form;

        if (searchForm.getSubmitType().equalsIgnoreCase("filter"))
        {
            // Filter the current chart
            filter(searchForm);
        }
        else
        {
            // Parse the search criteria
            // Since two primary criteria are selected from a list, do 'equals' search
            HashSet<Cohort> cohortSet = new HashSet<Cohort>();
            if (searchForm.getSelectedCohorts() != null && searchForm.getSelectedCohorts().length > 0)
            {
                for (int i = 0; i < searchForm.getSelectedCohorts().length; i++)
                {
                    Cohort cohort = SQLMgr.getCohort(searchForm.getSelectedCohorts()[i].trim());
                    if (cohort != null)
                    {
                        cohortSet.add(cohort);
                    }
                }
            }
            HashSet<Phenotypes> phenotypeSet = new HashSet<Phenotypes>();
            if (searchForm.getSelectedPhenotypes() != null && searchForm.getSelectedPhenotypes().length > 0)
            {
                for (int i = 0; i < searchForm.getSelectedPhenotypes().length; i++)
                {
                    Phenotypes phenotype = SQLMgr.getPhenotype(searchForm.getSelectedPhenotypes()[i].trim());
                    if (phenotype != null)
                    {
                        phenotypeSet.add(phenotype);
                    }
                }
            }
            // Following search criteria will be searched using 'like' search
            HashSet<SNP> snpSet = new HashSet<SNP>();
            if (searchForm.isChkSnp()
                && searchForm.getSnps() != null && !searchForm.getSnps().trim().isEmpty())
            {
                String[] tmp = searchForm.getSnps().trim().split("\n");
                String snps="";
                for (int i = 0; i < tmp.length; i++)
                {
                    //snpSet.addAll(SQLMgr.getSNPLike(tmp[i].replaceAll("rs", "").trim()));
                    SNP s= SQLMgr.getSNP(tmp[i].trim());
                    if(s != null){
                        snpSet.add(s);
                    }else{
                        snps+=tmp[i].trim()+"  ";
                    }
                }
                if(snps.length()>0){
                    ActionErrors errors = new ActionErrors();
                    errors.add("snps",new ActionMessage("errors.invalid.snp", snps));
                    saveErrors(request,errors);
                    forward = mapping.findForward("failure");
                    return forward;
                }

                
            }
            HashSet<Gene> geneSet = new HashSet<Gene>();
            if (searchForm.isChkGene()
                && searchForm.getGenes() != null && !searchForm.getGenes().trim().isEmpty())
            {
                String[] tmp = searchForm.getGenes().trim().split("\n");
                for (int i = 0; i < tmp.length; i++)
                {
                    geneSet.addAll(SQLMgr.getGeneLike(tmp[i].trim()));
                }
            }
            HashSet<String> chromoSet = new HashSet<String>();
            if (searchForm.isChkChromosome()
                && searchForm.getChromosomes() != null && !searchForm.getChromosomes().trim().isEmpty())
            {
                String[] tmp = searchForm.getChromosomes().trim().split("\n");
                for (int i = 0; i < tmp.length; i++)
                {
                    chromoSet.add(tmp[i].trim());
                }
            }
            // Get all 'like' pathways and add their genes to geneSet
            if (searchForm.isChkPathway()
                && searchForm.getPathways() != null && !searchForm.getPathways().trim().isEmpty())
            {
                String[] tmp = searchForm.getPathways().trim().split("\n");
                for (int i = 0; i < tmp.length; i++)
                {
                    for (KeggPathwayBean pathway : SQLMgr.getKeggPathwayLike(tmp[i].trim()))
                    {
                        for (KeggGeneBean gene : pathway.getGeneList())
                        {
                            geneSet.add(SQLMgr.getGene(gene.getGeneId()));
                        }
                    }
                }
            }
            // Create experiment list
            // This is a static list
            ArrayList<Integer> experimentList = new ArrayList<Integer>();
            experimentList.add(0);

            // do query
            // must be prepared statment to protect againt sql injection
            ArrayList<Values> valList = SQLMgr.getValues(new ArrayList<Cohort>(cohortSet), new ArrayList<Phenotypes>(phenotypeSet),
                                                         new ArrayList<Gene>(geneSet), new ArrayList<SNP>(snpSet), new ArrayList<String>(chromoSet),
                                                         experimentList, false);

            searchForm.setFullPvalueList(valList);

            // Filter pvalues according to the cut
            filter(searchForm);
        }

        if (searchForm.getPvalueList().isEmpty())
        {
            return mapping.findForward("noResults");
        }

        // Create Association chart
        createChart(searchForm, request);
        forward = mapping.findForward("display");

        response.setHeader("Cache-Control", "no-store");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);

        return forward;
    }

    private void filter(SearchAssociationForm form)
    {
        ArrayList<Values> pvalueList = form.getPvalueList();
        if (pvalueList == null)
        {
            pvalueList = new ArrayList<Values>();
            form.setPvalueList(pvalueList);
        }
        pvalueList.clear();

        if (form.isChkPvalue()
            && form.getCutValue() != null && !form.getCutValue().isEmpty())
        {
            for (Values value : form.getFullPvalueList())
            {
                if (meetsCriteria(value, true, form.getCutType(), form.getCutValue()))
                {
                    pvalueList.add(value);
                }
            }
        }
        else
        {
            pvalueList.addAll(form.getFullPvalueList());
        }
    }

    private void createChart(SearchAssociationForm form, HttpServletRequest request)
    {
        long start;
        long end;
        start = System.currentTimeMillis();
        XYSeries demo = new XYSeries(form.getCohorts().get(0) +" "+ form.getPhenotypes().get(0).getName(), false, true);

        // Create XYSerieses
        /*XYSeries capps_asthma = new XYSeries("Capps Asthma", false, true);
        XYSeries capps_atopy = new XYSeries("Capps Atopy", false, true);
        XYSeries capps_atopicAsthma = new XYSeries("Capps Atopic Asthma", false, true);
        XYSeries capps_ahr = new XYSeries("Capps AHR", false, true);

        XYSeries sage_asthma = new XYSeries("Sage Asthma", false, true);
        XYSeries sage_atopy = new XYSeries("Sage Atopy", false, true);
        XYSeries sage_atopicAsthma = new XYSeries("Sage Atopic Asthma", false, true);
        XYSeries sage_ahr = new XYSeries("Sage AHR", false, true);

        XYSeries slsj_asthma = new XYSeries("SLSJ Asthma", false, true);
        XYSeries slsj_atopy = new XYSeries("SLSJ Atopy", false, true);
        XYSeries slsj_atopicAsthma = new XYSeries("SLSJ Atopic Asthma", false, true);
        XYSeries slsj_ahr = new XYSeries("SLSJ AHR", false, true);

        XYSeries bus_asthma = new XYSeries("Bussleton Asthma", false, true);
        XYSeries bus_atopy = new XYSeries("Bussleton Atopy", false, true);
        XYSeries bus_atopicAsthma = new XYSeries("Bussleton Atopic Asthma", false, true);
        XYSeries bus_ahr = new XYSeries("Bussleton AHR", false, true);

        XYSeries comb_asthma = new XYSeries("Combined Asthma", false, true);
        XYSeries comb_atopy = new XYSeries("Combined Atopy", false, true);
        XYSeries comb_atopicAsthma = new XYSeries("Combined Atopic Asthma", false, true);
        XYSeries comb_ahr = new XYSeries("Combined AHR", false, true);*/

        final HashMap<Integer, ArrayList<Values>> tmpMap = new HashMap<Integer, ArrayList<Values>>();
        for (Values val : form.getPvalueList())
        {
          /*  if (val.getCohort().getCohortID() == GenotypeToolsManager.CAPPS)
            {
                addValuesToXYSeries(val, capps_asthma, capps_atopy, capps_atopicAsthma, capps_ahr, tmpMap, 0);
            }
            else if (val.getCohort().getCohortID() == GenotypeToolsManager.SAGE)
            {
                addValuesToXYSeries(val, sage_asthma, sage_atopy, sage_atopicAsthma, sage_ahr, tmpMap, 4);
            }
            else if (val.getCohort().getCohortID() == GenotypeToolsManager.SLSJ)
            {
                addValuesToXYSeries(val, slsj_asthma, slsj_atopy, slsj_atopicAsthma, slsj_ahr, tmpMap, 8);
            }
            else if (val.getCohort().getCohortID() == GenotypeToolsManager.BUSSELTON)
            {
                addValuesToXYSeries(val, bus_asthma, bus_atopy, bus_atopicAsthma, bus_ahr, tmpMap, 12);
            }
            else if (val.getCohort().getCohortID() == GenotypeToolsManager.COMBINED)
            {
                addValuesToXYSeries(val, comb_asthma, comb_atopy, comb_atopicAsthma, comb_ahr, tmpMap, 16);
            }*/

            addValuesToXYSeries(val, demo,tmpMap, 0);

        }

        // Creates and populates valueList and seriesCollection from tmpMap
        final XYSeriesCollection seriesCollection = new XYSeriesCollection();
        final ArrayList<ArrayList<Values>> valueList = new ArrayList<ArrayList<Values>>();
        final XYSeries[] seriesArray = {demo};
        for (int i = 0; i < seriesArray.length; i++)
        {
            if (seriesArray[i].getItemCount() != 0)
            {
                valueList.add(tmpMap.get(i));
                seriesCollection.addSeries(seriesArray[i]);
            }
        }

        JFreeChart chart = ChartFactory.createScatterPlot("", "Chromosome", "-log10(P-value)", seriesCollection, PlotOrientation.VERTICAL, true, true, true);

        final XYPlot plot = chart.getXYPlot();

        // add 1% significance level line
        ValueMarker valuemarker = new ValueMarker(2D);
        valuemarker.setLabelOffsetType(LengthAdjustmentType.EXPAND);
        valuemarker.setPaint(Color.green);
        valuemarker.setLabel("1% Level");
        valuemarker.setLabelAnchor(RectangleAnchor.BOTTOM_RIGHT);
        valuemarker.setLabelTextAnchor(TextAnchor.TOP_RIGHT);
        plot.addRangeMarker(valuemarker);

        // add 5% significance level line
        ValueMarker valuemarker5 = new ValueMarker(1.301D);
        valuemarker5.setLabelOffsetType(LengthAdjustmentType.EXPAND);
        valuemarker5.setPaint(Color.green);
        valuemarker5.setLabel("5% Level");
        valuemarker5.setLabelAnchor(RectangleAnchor.BOTTOM_RIGHT);
        valuemarker5.setLabelTextAnchor(TextAnchor.TOP_RIGHT);
        plot.addRangeMarker(valuemarker5);

        ValueAxis axis = plot.getRangeAxis();
        axis.setAutoRange(false);
        axis.setRange(0, 3.5);

        ValueAxis dAxis = plot.getDomainAxis();
        dAxis.setAutoRange(false);
        dAxis.setRange(0, 22);

        //add custon url and tooltip generators
        final XYItemRenderer renderer1 = plot.getRenderer();
        renderer1.setSeriesPaint(0, Color.red);
        SnpXYURLGenerator gen = new SnpXYURLGenerator();
        gen.setValueList(valueList);
        renderer1.setURLGenerator(gen);
        renderer1.setBaseToolTipGenerator(gen);
        gen.setUrlPrefix(request.getContextPath() + "/snpSummary.do?rsNumber=");

        final ChartRenderingInfo info = new ChartRenderingInfo(new StandardEntityCollection());

        BufferedImage image = chart.createBufferedImage(740, 500, info);
        request.getSession().setAttribute("Chart", image);
        request.getSession().setAttribute("ChartRenderingInfo", info);

        end = System.currentTimeMillis();
        System.out.println("Processing time: " + (end - start) + " milliseconds. ");
    }

    private void addValuesToXYSeries(Values values, XYSeries demo,
                                     HashMap<Integer, ArrayList<Values>> map, Integer startIndex)
    {
        Double chromosome = Double.parseDouble(values.getSnp().getChromosome().trim());
        Double pvalue = Math.abs(Math.log10(values.getCorrectedPValue()));

        Integer index = -1;
       /* if (values.getPhenotype().getName().equalsIgnoreCase("Asthma"))
        {
            asthma.add(chromosome, pvalue);
            index = startIndex;
        }
        else if (values.getPhenotype().getName().equalsIgnoreCase("Atopy"))
        {
            atopy.add(chromosome, pvalue);
            index = startIndex + 1;
        }
        else if (values.getPhenotype().getName().equalsIgnoreCase("Atopic Asthma"))
        {
            atopicAsthma.add(chromosome, pvalue);
            index = startIndex + 2;
        }
        else if (values.getPhenotype().getName().equalsIgnoreCase("AHR"))
        {
            ahr.add(chromosome, pvalue);
            index = startIndex + 3;
        }*/
        demo.add(chromosome, pvalue);
        index=startIndex+3;

        if (index > 0)
        {
            ArrayList<Values> valuesList = map.get(index);
            if (valuesList == null)
            {
                valuesList = new ArrayList<Values>();
                map.put(index, valuesList);
            }
            valuesList.add(values);
        }
    }

    private boolean meetsCriteria(Values value, boolean isCorrected, String cutType, String cutVal)
    {
        Double pValue;

        // Check whether to use corrected value or uncorrected value
        if (isCorrected)
        {
            pValue = Double.valueOf(value.getCorrectedPValue());
        }
        else
        {
            pValue = Double.valueOf(value.getUncorrectedPValue());
        }

        // Figure out whether the value meets the criteria
        if (pValue != null)
        {
            double cut = Double.parseDouble(cutVal);
            if (cutType.equalsIgnoreCase(">"))
            {
                if (pValue > cut)
                {
                    return true;
                }
            }
            else if (cutType.equalsIgnoreCase("<"))
            {
                if (pValue < cut)
                {
                    return true;
                }
            }
            else if (cutType.equalsIgnoreCase(">="))
            {
                if (pValue >= cut)
                {
                    return true;
                }
            }
            else if (cutType.equalsIgnoreCase("<="))
            {
                if (pValue <= cut)
                {
                    return true;
                }
            }
        }
        return false;
    }
}